package com.gentics.labs.sms;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.gentics.api.lib.datasource.DatasourceInfo;
import com.gentics.api.lib.datasource.WriteableDatasource;
import com.gentics.api.lib.expressionparser.ExpressionParser;
import com.gentics.api.lib.expressionparser.filtergenerator.DatasourceFilter;
import com.gentics.api.lib.resolving.Changeable;
import com.gentics.api.portalnode.connector.PortalConnectorFactory;
import com.gentics.contentnode.datasource.CNWriteableDatasource;
import com.gentics.lib.base.MapResolver;
import com.gentics.lib.etc.StringUtils;

public class SMSReceiver extends HttpServlet{
	private static final long serialVersionUID = -7005163056414141831L;

	private Logger logger = Logger.getLogger(this.getClass());
	private String driverClass = "";
	private String type = "";
	private WriteableDatasource twitter_ds = null;	
	private WriteableDatasource user_ds = null;
	private String smsobjecttype = "";
	private String userobjecttype = "";
	private String twitter_pcr_url = "";
	private String user_pcr_url = "";
	
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		
		driverClass = config.getInitParameter("datasourceDriver");
		type = config.getInitParameter("datasourceType");
		
		twitter_pcr_url = config.getInitParameter("cr_tweet_url");
		user_pcr_url = config.getInitParameter("cr_user_url");
		
		smsobjecttype = config.getInitParameter("smsobjecttype");
		userobjecttype = config.getInitParameter("userobjecttype");
		
		//Prepare Datasource where to store SMS
		Map<String, Object> twitterDatasourceProps = new HashMap<String, Object>();
		twitterDatasourceProps.put("driverClass", driverClass);
		twitterDatasourceProps.put("type", type);
		twitterDatasourceProps.put("url", twitter_pcr_url);
		
		// Retrieve the Writeable Datasource.
		twitter_ds = PortalConnectorFactory.createWriteableDatasource(twitterDatasourceProps);
		
		// if no datasource is defined for user-repository, use the same as for tweets
		if (StringUtils.isEmpty(user_pcr_url)){
			user_ds = twitter_ds;
		}else{
			//Prepare Datasource where to read users
			Map<String, Object> userDatasourceProps = new HashMap<String, Object>();
			userDatasourceProps.put("driverClass", driverClass);
			userDatasourceProps.put("type", type);
			userDatasourceProps.put("url", user_pcr_url);
			
			// Retrieve the Writeable Datasource.
			user_ds = PortalConnectorFactory.createWriteableDatasource(userDatasourceProps);
		}
		
		if(twitter_ds == null) {
			this.logger.error("Could not initialize datasource for storing SMS.");
		}
		
		if(user_ds == null) {
			this.logger.error("Could not initialize datasource for reading user information.");
		}
	}
	
	
	/**
	 * Wrapper Method for the doGet and doPost Methods
	 * 
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 */
	public void doService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//this.log.debug("Request:" + request.getQueryString());
		///sms.php?from=4369912677513&to=43676800676776&txt=G1X%20hallo&date=2008.11.06&time=15:59:48&route=1789 HTTP/1.0" 200 1 "-" "-"
		String from = (String) request.getParameter("from");
		String to = (String) request.getParameter("to");
		String text = (String) request.getParameter("txt");
		String date = (String) request.getParameter("date");
		String time = (String) request.getParameter("time");	
		String userid =null;
		String [] preload = {"contentid","mobile"};
		
		// Find user for sender mobile number
		try {
			DatasourceFilter filter = user_ds.createDatasourceFilter(ExpressionParser.getInstance().parse("object.obj_type == data.usertype && !isempty(object.mobile)"));
			Map<String,String> dataMap = new HashMap<String,String>();
			dataMap.put("usertype", userobjecttype);
			filter.addBaseResolvable("data", new MapResolver(dataMap));
			
			Collection result = user_ds.getResult(filter, preload);
			
			Iterator iterator = result.iterator();
			Changeable userobj = null;
			
			logger.debug("from-mobile: "+from);
			while (iterator.hasNext() && userobj == null) {
				Changeable obj = (Changeable) iterator.next();
				
				String oldmobile = (String) obj.getProperty("mobile");
				String newmobile = oldmobile.replace(" ", "").replace ("+","");
				//obj.setProperty("mobile", newmobile);#
				if (from.equals(newmobile)) {
					userobj = obj;
					userid = userobj.getProperty("contentid").toString();
					logger.debug("found user to number: "+userid);
				}
			}
			
		} catch (Exception e){
			logger.error("Error while searching for corresponding user");
			//TODO
		}
		
		if (StringUtils.isEmpty(userid)){
			logger.debug("no user found to number");
		}
		
		// Create Date Object for date & time
		Date dateobj = null;
		SimpleDateFormat dateform = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
		
		logger.debug("date: "+date+"; time: "+time);
		try {
			dateobj = dateform.parse(date+" "+time);
		} catch(Exception e) {
			logger.error("Problem while creating Date: "+e.getMessage());
			dateobj = new Date();
		}
		
		// Create a new object, by first putting all attributes into a hash map..
		Map newSMS = new HashMap();
		newSMS.put("obj_type", smsobjecttype); // Create an SMS object
		text = text.replaceAll("\\\\&quot;", "\\&quot;");
		text = text.replaceAll("\\\\\"", "\"");
		text = text.replaceAll("\\\\'", "\'");
		
		if (StringUtils.isEmpty(from) || StringUtils.isEmpty(date) || StringUtils.isEmpty(time) ){
			logger.error("Not all parameters were transmitted!");
			return;
		}
			
		//get original Text and detect if we get a multipart message
		//TODO: do real SMS encoding
		//in multipart SMS we get 7Bytes:
		//byte[0] == -23
		//byte[1] == 64
		//byte[2] == -8
		//byte[3] supposed to be length (example 3 messages)
		//byte[4] supposed to be the message ID
		//byte[5] supposed to be partnumber
		//byte[6] supposed to be the sperator
		//the sms metadata seems to be coded in 7bit not in 8bit therefore it can be tricky to read out in Java
		//see http://www.activexperts.com/xmstoolkit/sms/multipart/
		String html_unescaped = org.apache.commons.lang.StringEscapeUtils.unescapeHtml(text);
		byte[] text_chars = html_unescaped.getBytes();
		byte[] code_chars = new String(text_chars).substring(0, 3).getBytes();
		byte[] code_match = {-23,64,-8};
		if(new String(code_chars).equals(new String(code_match))){
			byte[] metadata = (new String(text_chars)).substring(0, 7).getBytes();
			byte[] message = (new String(text_chars)).substring(7).getBytes();
			logger.debug("Detected Multipart SMS cutting off metadata.\n" +
					"metadata:"+metadata+"\n" +
					"textdata:"+message+"\n");
			text=org.apache.commons.lang.StringEscapeUtils.escapeHtml(new String(message));
		}
		
		
		// Set all attributes.
		newSMS.put("mobile", from);
		newSMS.put("message", text);
		newSMS.put("date", dateobj);
		newSMS.put("userid_sms", userid);
		
		// Now we have all attributes set, so create the actual Changeable. (Content Object)
		try {
			Changeable changeable = twitter_ds.create(newSMS);
			
			// We now have a Changeable (Content Object) - So insert it into the datasource.
			DatasourceInfo info = twitter_ds.insert(Collections.singleton(changeable));
			((CNWriteableDatasource)twitter_ds).setLastUpdate();
			
		} catch (Exception e) {
			//TODO Logging
			e.printStackTrace();
			logger.error("Error while storing SMS in datasource: "+ e.getMessage());
		}

	}

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doService(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doService(request, response);
	}
}